# ANSI Text Image Library

#   Akalabeth Re-Bourne
#   Copyright (C) 2008 Norman B. Lancaster
#
#    This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#   along with this program.  If not, see http://www.gnu.org/licenses/

# Configuration variables
declare -r aiCacheFile="./data/ansimg.dat"
declare -r aiImagePath="./data/ansimg/"

# Globals
declare -a aiImageCache

# Compile a text file into an ANSI image
function AICompileImage
{
	local line
	local lineNum=0

	# Begin all images, even ones with no readable lines, with "Save Cursor"
	# and always set the default colors for every tile. We put the "Save Curosr"
	# bit after the default color bit due to an issue with the way XTerm handles
	# the "Restore Cursor" code. Specifically it restores the color too. This
	# makes sure that it always restores to the default colors.
	echo -nE "\033[97;40m\033[s"

	# Image parsing loop
	while read -r line; do
		# Start every line with the default colors. This is mainly due to an
		# issue with the way XTerm handles the "Restore Cursor" code.
		# Specifically XTerm will restore the color from when the cursor was
		# saved, and the GNOME terminal does not. This forces the GNOME terminal
		# to the default colors on each line.
		echo -nE "\033[97;40m"
		
		# Escape backslash litterals
		line="${line//\\/\\\\}"
		# Embeded color codes
		line="${line//c10/\033[92m}"
		line="${line//c11/\033[93m}"
		line="${line//c12/\033[94m}"
		line="${line//c13/\033[95m}"
		line="${line//c14/\033[96m}"
		line="${line//c15/\033[97m}"
		line="${line//c0/\033[30m}"
		line="${line//c1/\033[31m}"
		line="${line//c2/\033[32m}"
		line="${line//c3/\033[33m}"
		line="${line//c4/\033[34m}"
		line="${line//c5/\033[35m}"
		line="${line//c6/\033[36m}"
		line="${line//c7/\033[37m}"
		line="${line//c8/\033[90m}"
		line="${line//c9/\033[91m}"
		# Transparent pixel escapement
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[48C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[47C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[46C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[45C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[44C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[43C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[42C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[41C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[40C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[39C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[38C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[37C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[36C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[35C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[34C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[33C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[32C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[31C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[30C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[29C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[28C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnnn/\033[27C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnnn/\033[26C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnnn/\033[25C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnnn/\033[24C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnnn/\033[23C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnnn/\033[22C}"
		line="${line//nnnnnnnnnnnnnnnnnnnnn/\033[21C}"
		line="${line//nnnnnnnnnnnnnnnnnnnn/\033[20C}"
		line="${line//nnnnnnnnnnnnnnnnnnn/\033[19C}"
		line="${line//nnnnnnnnnnnnnnnnnn/\033[18C}"
		line="${line//nnnnnnnnnnnnnnnnn/\033[17C}"
		line="${line//nnnnnnnnnnnnnnnn/\033[16C}"
		line="${line//nnnnnnnnnnnnnnn/\033[15C}"
		line="${line//nnnnnnnnnnnnnn/\033[14C}"
		line="${line//nnnnnnnnnnnnn/\033[13C}"
		line="${line//nnnnnnnnnnnn/\033[12C}"
		line="${line//nnnnnnnnnnn/\033[11C}"
		line="${line//nnnnnnnnnn/\033[10C}"
		line="${line//nnnnnnnnn/\033[9C}"
		line="${line//nnnnnnnn/\033[8C}"
		line="${line//nnnnnnn/\033[7C}"
		line="${line//nnnnnn/\033[6C}"
		line="${line//nnnnn/\033[5C}"
		line="${line//nnnn/\033[4C}"
		line="${line//nnn/\033[3C}"
		line="${line//nn/\033[2C}"
		line="${line//n/\033[1C}"
		# Space escapement, use this when you need to start a line with a space
		line="${line//s/\040}"
		
		# If this is not the first line, echo "Cursor Restor, Down $lineNum"
		if (( $lineNum != 0 )); then
			echo -nE "\033[u\033[${lineNum}B"
		fi

		# And echo out compiled line
		echo -nE "$line"
		(( lineNum++ ))
	done

	return 0
}

# Loads the image cache from disk if present and current, or builds the cache
function AILoadCache
{
	local fname
	
	# Cache is up to date, load it up as-is
	if AIIsCacheValid "$aiCacheFile" "$aiImagePath"; then
		AILoadCacheImages < "$aiCacheFile"
	# Cache is present but stale, load the cache, reload all files that have
	# been changed, then re-write the cache file.
	elif (( $? == 2 )); then
		AILoadCacheImages < "$aiCacheFile"
		echo "Updating image cache"
		AIUpdateCache "$aiCacheFile" "$aiImagePath"
		echo "Writing cache to disk"
		AIWriteCache > "$aiCacheFile"
	else
		echo "Building image cache"
		AIBuildCache "$aiImagePath"
		echo "Writing cache to disk"
		AIWriteCache > "$aiCacheFile"
	fi
	return 0
}

function AIUpdateCache
{
	local fname
	
	for fname in ${2}*; do
		# Skip subdirectories
		if [[ -d "$fname" ]]; then continue; fi
	
		# Strip directory information off so we can look at the file name
		fname="${fname#${2}}"
	
		# And if the name is alphanumeric, check the file times
		if [[ $fname =~ ^[0-9]+$ ]]; then
			# If there is a file in the image path that is newer than the cache
			# file, the cache file needs to be updated.
			if [[ "${2}${fname}" -nt "$1" ]]; then
				# Progress line indicator
				echo -n "."
				# Compile image file and add to the cache
				aiImageCache[$fname]=`AICompileImage < "${2}${fname}"`
			fi
		fi
	done
	echo
	
	return 0
}

# Returns zero only if the cache disk file is present, readable, and up to date
# $1 Cache file to check against
# $2 Directory containing the source image files
function AIIsCacheValid
{
	local fname
	
	# If the cache file does not exist, it can't be valid
	if [ ! -r "$1" ]; then
		return 1
	# If the source image directory does not exist the cache cannot be updated
	elif [ ! -d "$2" ]; then
		return 0
	fi
	
	# Compare file modification times in the image source directory with the
	# propsed cache file.
	for fname in ${2}*; do
		# Skip subdirectories
		if [[ -d "$fname" ]]; then continue; fi
		
		# Strip directory information off so we can look at the file name
		fname="${fname#${2}}"
		
		# And if the name is alphanumeric (and therefore is a candidate to load
		# into the cache), check the file times.
		if [[ $fname =~ ^[0-9]+$ ]]; then
			# If there is a file in the image path that is newer than the cache
			# file, the cache file needs to be updated.
			if [[ "${2}${fname}" -nt "$1" ]]; then return 2; fi
		fi
	done
	
	return 0
}

# Populates the cache from the cache file
function AILoadCacheImages
{
	local idx line
	
	# Line loading loop
	while read -r idx line; do
		aiImageCache[$idx]="$line"
	done
	
	return 0
}

# Builds the cache from the source files
# $1 Directory containing the image files
function AIBuildCache
{
	local fname
	
	# Look at all files inside $1
	for fname in ${1}*; do
		# Skip subdirectories
		if [[ -d "$fname" ]]; then continue; fi
		
		# Strip directory information off so we can look at the file name
		fname="${fname#${1}}"
		
		# And if the name is alphanumeric, load it
		if [[ $fname =~ ^[0-9]+$ ]]; then
			# Progress line indicator
			echo -n "."
			# Compile image file and add to the cache
			aiImageCache[$fname]=`AICompileImage < "${1}${fname}"`
		else
			echo "WW Skipping $fname because the name is not numeric" 1>&2
		fi
	done
	echo
	
	return 0
}

# Writes the cache file
function AIWriteCache
{
	local idx
	
	# Write out all valid array indecies to the cache
	for idx in ${!aiImageCache[*]}; do
		# Progress line indicator
		echo -n "." 1>&2
		# Write out the image string
		echo -E "${idx} ${aiImageCache[$idx]}"
	done
	echo 1>&2
	
	return 0
}

